home *** CD-ROM | disk | FTP | other *** search
/ Technotools / Technotools (Chestnut CD-ROM)(1993).ISO / lang_c / cug233 / make.c < prev    next >
Text File  |  1987-06-30  |  13KB  |  527 lines

  1. /* 
  2.  *     Program Name    : MAKE.C
  3.  *    Author        : Allen Holub
  4.  *    Implementor    : Kenji Hino
  5.  *    Compiler    : Microsoft ver. 4.0, Lattice C ver. 3
  6.  *    Description    : MAKE is the program that maintains programs.
  7.  *              MAKE takes resposibility for recompiling or linking
  8.  *              any source or object codes if any of them has 
  9.  *              updated.
  10.  */ 
  11.  
  12. #include "make.h"
  13.  
  14. void main(argc, argv)
  15. char     **argv;
  16. int     argc;
  17. {
  18. int dependancies(), make(), err();
  19.  
  20.     if( !(Makefile = fopen(MAKEFILE, "r")) )
  21.         err("can't open %s\n", MAKEFILE);
  22.         
  23.     if( !dependancies() )
  24.         err("Nothing to make");
  25.     else
  26.         make( argc > 1 ? argv[1] : First);
  27.     
  28. } /* main end */
  29.  
  30.  
  31. char    *gmem( numbytes )
  32. int     numbytes;
  33. {
  34.     /* Get numbytes from malloc. Print an error messages and 
  35.        abort if malloc fails, otherwise return a pointer to
  36.        the memory.
  37.     */
  38.     
  39.     char     *calloc();
  40.     char     *p;
  41.     int    err();
  42.     
  43.     if(  !( p = calloc(1, numbytes) ))
  44.         err("Out of memory");
  45.     return p;
  46. } /* gmem end */
  47.  
  48. char     **stov(str, maxvect)
  49. char    *str;
  50. int     maxvect;
  51. {
  52.     /* "Str" is a string of words seperated from each other by
  53.        white space. Stov returns an argv-like array of pointers 
  54.        to chracter pointers, one to each word in the original
  55.        string. The white-space in the original string is replaced
  56.        with nulls. The array of pointers is null-terminated
  57.        array. The program is aborted of it can't get memory.
  58.     */
  59.     
  60.     char **vect, **vp;
  61.     char *gmem();
  62.  
  63.     vp = vect = (char **) gmem( (maxvect + 1) * sizeof(str) );
  64.     while( *str && --maxvect >= 0 )
  65.     {
  66.         skipwhite(str);
  67.         *vp++ = str;
  68.         skipnonwhite(str);
  69.         if(*str)
  70.             *str++ = 0;
  71.     }
  72.     *vp = 0;
  73.     return(vect);
  74. } /* stov end */
  75.  
  76. long    gtime( file )
  77. char     *file;
  78. {
  79.     /* Return the time and date for file.
  80.        
  81.       The DOS time and date are concatenated to form one
  82.       large number. Note that the high bit of this number
  83.       will be set to 1 for all dates after 2043, which will
  84.       cause the date comparisons done in make() to not work.
  85.       THIS ROUTINE IS NOT PORTABLE (because it assumes a 32 bit
  86.       long).
  87.       
  88.     */
  89.  
  90.     long time;
  91.     int fd;
  92.     int open(), err(), close();
  93.  
  94. #ifdef    MICRO_C
  95.     struct stat buf;
  96.     int fstat();
  97.     int result;
  98. #endif
  99.  
  100. #ifdef    LATTICE_C
  101.     long getft();
  102. #endif
  103.  
  104.     if ( (fd = open (file, O_RDONLY)) == -1 )
  105.         return DEFTIME;
  106.  
  107. #ifdef    MICRO_C
  108.     if ( (result = fstat (fd, &buf)) == -1 )
  109.         err("DOS returned error from date/time request");
  110.     time = buf.st_atime;
  111. #endif
  112.  
  113. #ifdef    LATTICE_C
  114.     /* LATTICE uses a different time structure from one returned by */
  115.     /* gmtime() so that it doesn't show the same file time as one you get */
  116.     /* by "dir" command. However, it still can tell the difference */
  117.     /* between new and old files. */
  118.  
  119.  
  120.     if ( (time = getft (fd)) == -1 )
  121.         err("DOS returned error from date/time request");
  122. #endif
  123.  
  124.     if ( close (fd) )
  125.         err("DOS returned error from file close request");
  126.  
  127.     return time;
  128. } /* gtime end */
  129.  
  130. TNODE    *makenode()
  131. {
  132.     /* Create a TNODE, filling it from the makefile
  133.        and return a pointer to it. Return NULL if there are no more
  134.        objects in the makefile.
  135.     */
  136.     
  137.     char     *line, *lp;
  138.     TNODE    *nodep;
  139.     char *getline(), **stov(), **getblock(), *gmem();
  140.     int    err();
  141.     long    gtime();
  142.  
  143.     /* First, skip past any blank lines or comment lines.
  144.        Return  NULL if we reach the end of file
  145.     */
  146.     
  147.     do{
  148.         if( (line = getline(MAXLINE, Makefile)) == NULL )
  149.             return( NULL );
  150.     } while ( *line == 0 || *line == COMMENT );
  151.     
  152.     /* At this point we've gotten what should be the dependancy
  153.        line. Position lp to point at the colon.
  154.     */
  155.  
  156.     for( lp  = line; *lp && *lp != ':'; lp++)
  157.         ;
  158.         
  159.     /* If we find the colon position, lp to point at the first
  160.        non-white character following the colon.
  161.     */
  162.     
  163.     if( *lp != ':' )
  164.         err("missing ':'");    /* This will abort the program */
  165.     else
  166.         for( *lp++ = 0; iswhite(*lp) ; lp++)
  167.         ;
  168.     
  169.     /* Allocate and initialize the TNODE */
  170.     
  171.     nodep            = (TNODE *) gmem( sizeof(TNODE) );
  172.     nodep->being_made    = line;
  173.     nodep->time        = gtime( line );
  174.     nodep->depends_on    = stov( lp, MAXDEP );
  175.     nodep->do_this        = getblock( Makefile );
  176.     
  177.     return( nodep );
  178. } /* makenode end */
  179.  
  180. int dependancies()
  181. {
  182.     /* Manufacture the binary tree of objects to make. First
  183.        is a pointer to the first target file listed in the 
  184.        makefile (i.e. the one to make if one isn't explicitly
  185.        given on the command line). Root is the tree's root pointer.
  186.     */
  187.     
  188.     TNODE    *node, *makenode();
  189.     int     err(), tree();
  190.  
  191.     if( node = makenode() )
  192.     {
  193.         First = node->being_made;
  194.         
  195.         if( !tree(node, &Root) )
  196.             err("Can't insert first node into tree !!!\n");
  197.         
  198.         while( node = makenode() )
  199.             if( !tree(node, &Root) )
  200.                 free(node);
  201.             return 1;
  202.     }
  203.     return 0;
  204. } /* dependancies end */
  205.  
  206.  
  207. char     *getline( maxline, fp)
  208. int maxline;
  209. FILE *fp;
  210. {
  211.     /* Get a line from the stream pointed to by fp.
  212.        "Maxline" is the maximum input line size ( including the
  213.        terminating null. A \ at the end of line is
  214.        recognized as a line continuation, ( the lines
  215.        are concatenated). Buffer space is gotten from malloc.
  216.        If a line is longer than maxline it is truncated (i.e.
  217.        all characters from the maxlineth until a \n or EOF is
  218.        encountered are discarded.
  219.        
  220.       Return: NULL on a malloc failure or the end of file.
  221.             A pointer to the malloced buffer on success.
  222.     */
  223.     
  224.     static char    *buf;
  225.     register char     *bp;
  226.     register int     c, lastc;
  227.     char         *malloc(), *strcpy();
  228.  
  229.  
  230.     /* Two buffers are used, Here, we are getting a worst-case buffer
  231.        that will hold the longest possible line. Later on we'll copy
  232.        the string into a buffer that's the correct size.
  233.     */
  234.     
  235.     if( !(bp = buf = malloc(maxline)) )
  236.         return  NULL;
  237.     while(1)
  238.     {
  239.         /* Get the line from fp. Terminate after maxline
  240.            characters and ignore \n following a \.
  241.         */
  242.         
  243.         Inputline++;         /* Update input line number */
  244.         for( lastc=0; (c = fgetc(fp)) != EOF && c!='\n'; lastc = c)
  245.             if( --maxline > 0 )
  246.                 *bp++ = c;
  247.         if( !(c == '\n' && lastc == '\\') )
  248.             break;
  249.         else if(maxline > 0)    /* erase the \ */
  250.             --bp;
  251.     }
  252.     *bp = 0;
  253.         
  254.     if( (c == EOF && bp == buf) || !(bp = malloc((bp-buf) + 1)) )
  255.     {
  256.         /* If EOF was the first character on the line or
  257.            malloc fails when we try to get a buffer, quit.
  258.         */
  259.             
  260.         free(buf);
  261.         return( NULL );
  262.             
  263.     }
  264.         
  265.     strcpy (bp, buf);    /* Copy the worst-case buffer to the one */
  266.                 /* that is the correct size and ... */
  267.     free ( buf );        /* free the original, worst-case buffer, */
  268.     return ( bp );        /*  returning a pointer to the copy */
  269. } /* getline end */
  270.     
  271.  
  272. char     **getblock( fp )
  273. FILE *fp;
  274. {
  275.     /* Get a block from standard input. A block is a sequences of
  276.        lines terminates by a blank line. The block is returned as
  277.        an array of pointers to strings. At most MAXBLOCK lines can
  278.        be in a block. Leading white space is stripped.
  279.     */
  280.     
  281.     char    *p, *lines[MAXBLOCK], **blockv = lines, *gmem();
  282.     int     blockc  = 0;
  283.     char     *memcpy(), *getline(), *gmem();
  284.     int     err();
  285.  
  286.     do {
  287.         if( !( p = getline(MAXLINE, Makefile) ))
  288.             break;
  289.             
  290.         skipwhite(p);
  291.         
  292.         if( ++blockc <= MAXBLOCK )
  293.             *blockv++ = p;
  294.         else
  295.             err("action too long (max = %d lines)", MAXBLOCK);
  296.     } while(*p);
  297.     
  298.     /* Copy the blockv array into a safe place. Since the array
  299.        returned by getblock is NULL terminated, we need to 
  300.        increment block first.
  301.     */
  302.     
  303.     blockv = (char **) gmem( (blockc + 1) * sizeof(blockv[0]) );
  304.     memcpy( blockv,lines, blockc * sizeof(blockv[0]) );
  305.     blockv[blockc] = NULL;
  306.     
  307.     return blockv;
  308. } /* getblock end */
  309.  
  310.  
  311. int err( msg, param)
  312. char *msg;
  313. int param;
  314. {
  315.     /* Print the error message and exit the program. */
  316.     
  317.     fprintf(stderr, "Mk (%s line %d): ", MAKEFILE, Inputline);
  318.     fprintf(stderr, msg, param);
  319.     exit(1);
  320.     
  321. } /* err end */
  322.  
  323. int serr( msg, param )
  324. char *msg, *param;
  325. {
  326.     /* same as err() except the parameter is a string pointer
  327.        instead pf an int.
  328.     */
  329.     
  330.     fprintf(stderr, "Mk (%s line %d): ", MAKEFILE, Inputline);
  331.     fprintf(stderr, msg, param);
  332.     exit(1);
  333. } /* serr end */
  334.  
  335. int make(what)
  336. char *what;
  337. {    
  338.     /* Actually do the make. the dependancy tree is descended
  339.        recursively and of required, the dependancies are 
  340.        adjusted. Return 1 if anything was done, 0 otherwise.
  341.     */
  342.     
  343.     TNODE        *snode;        /* Source file node pointer */
  344.     TNODE        *dnode;        /* Dependant file node pointer */
  345.     int        doaction = 0;    /* If true do the action */
  346.     static char     *zero = (char *)0;
  347.     char        **linev = &zero;
  348.     TNODE        *find();
  349.     int         make(), serr(), system();
  350.  
  351. #ifdef    DEBUG
  352.     static int    recurlev = 0;    /* Recursion level */
  353.     
  354.     printf("make (lev %d): making <%s> \n", recurlev, what );
  355. #endif
  356.  
  357.     if( !(snode = find(what, Root)) )
  358.         serr("Don't know how to make <%s>\n", what);
  359.         
  360.     if( !*(linev = snode->depends_on) )    /* If no dependancies */
  361.         doaction++;            /* always do the action. */
  362.     
  363.     for( ; *linev; linev++)        /* Process each dependancy */
  364.     {
  365. #ifdef DEBUG
  366.         recurlev++;
  367. #endif
  368.         make( *linev );
  369. #ifdef DEBUG
  370.         recurlev--;
  371. #endif
  372.  
  373.         if( !(dnode = find(*linev, Root)) )
  374.             serr("Don't know how to make <%s>\n", *linev);
  375. #ifdef DEBUG
  376.         printf("make (lev %d):source   file ", recurlev);
  377.         ptime( what, snode->time );
  378.         printf("make (lev %d):depenadant file ", recurlev);
  379.         ptime( *linev, dnode->time);
  380. #endif 
  381.         if( snode->time <= dnode->time )
  382.         {
  383.             /* If source node is older than (time is less than)
  384.                dependant node, do something. If the times are
  385.                equal, assume that neither file exists but that
  386.                the action will create them, and do the action.
  387.             */
  388.             
  389. #ifdef DEBUG
  390.             printf("make (lev %d): %s older than %s\n",
  391.             recurlev, what, *linev);
  392. #endif
  393.             doaction++;
  394.         }
  395. #ifdef DEBUG
  396.         else
  397.             printf("make (lev %d): %s younger than %s\n", recurlev,
  398.             what, *linev );
  399. #endif
  400.     }
  401.     if( doaction )
  402.     {
  403. #ifdef DEBUG
  404.             printf("make (lex, %d): doing action:\n",
  405.             recurlev, *linev, what);
  406. #endif
  407.             for( linev  = snode->do_this; *linev; linev++)
  408.             {
  409.                 printf("%s\n", *linev); /* Echo action to screen */
  410. #ifndef DEBUG
  411.             if( system(*linev) )
  412.                 serr("Can't process <%s>\n", *linev);
  413.  
  414. #endif
  415.             /* Change the source file's time to refelect
  416.                any modification.
  417.             */
  418.             
  419.             snode->time = gtime( snode->being_made );
  420.         }
  421.     }
  422. #ifdef DEBUG
  423.     printf("make (lev %d): exiting\n", recurlev );
  424. #endif 
  425.     return doaction;
  426. } /* make end */
  427.  
  428.  
  429. TNODE    *find( key, root )
  430. char     *key;
  431. TNODE    *root;
  432. {
  433.     /* If key is in the tree pointed to by root, return a pointer
  434.        to it, else reutrn 0.
  435.     */
  436.     
  437.     register int     notequal ;
  438.     TNODE        *find();
  439.     int         strcmp();
  440.  
  441.     if( !root )
  442.         return 0;
  443.  
  444.     if( !(notequal = strcmp(root->being_made, key)) )
  445.         return (root);
  446.         
  447.     return( find(key, (notequal > 0) ? root->lnode : root->rnode) );
  448. } /* find end */
  449.  
  450.  
  451. int    tree(node, rootp)
  452. TNODE *node, **rootp;
  453. {
  454.     /* If node's key is in the tree pointed to by rootp, return 0
  455.        else put it into the tree and return 1.
  456.     */
  457.     
  458.     register int     notequal;
  459.     int        tree(),strcmp();
  460.  
  461.     if( *rootp == NULL )
  462.     {
  463.         *rootp = node;
  464.         return 1;
  465.     }
  466.     
  467.     if ( !(notequal = strcmp( (*rootp)->being_made, node->being_made)) )
  468.         return 0;
  469.         
  470.     return( tree( node, notequal > 0 ? &((*rootp)->lnode)
  471.                     : &((*rootp)->rnode)) );
  472. } /* tree end */
  473.  
  474.  
  475.  
  476. #ifdef DEBUG
  477.      /* Debugging Routine */
  478.      
  479. int     ptime( file, time )
  480. char *file;
  481. long time;
  482.  {
  483.      /* Print out the time and date field of a TNODE
  484.         File is the file name.
  485.      */
  486.  
  487.      char *ctime();
  488.      
  489.      printf("%s; ", file);
  490.      printf("%s", ctime(&time));
  491.      printf("\n");
  492. } /* ptime end */
  493.  
  494. pnode( node )
  495. TNODE *node;
  496. {
  497.     /* Print out the tree node pointed to by "node". */
  498.     
  499.     char **linev;
  500.     printf("+----------------------------------------\n");
  501.     printf("|   node at 0x%x\n", node);
  502.     printf("+----------------------------------------\n");
  503.     printf("|   lnode  = 0x%x, rnode = 0x%x \n ", node->lnode, node->rnode);
  504.     printf("|   time   = 0x%lx, =", node->time);
  505.     printf("|   target = <%s>\n", node->being_made);
  506.     printf("|   dependancies:\n");
  507.     for( linev = node->depends_on; *linev; printf("|\t%s\n", *linev++));
  508.     printf("|   actions:\n");
  509.     for( linev = node->do_this; *linev; printf("|\t%s\n", *linev++));
  510.     printf("+----------------------------------------\n");
  511. } /* pnode end */
  512.  
  513. trav( root )
  514. TNODE *root;
  515. {
  516.     /* Do an in-order traversal of the tree, printing the
  517.        node's contents as you go.
  518.     */
  519.     
  520.     if( root == NULL )
  521.         return;
  522.     trav( root->lnode );
  523.     pnode( root );
  524.     trav( root->rnode );
  525. } /* trav end */
  526. #endif
  527.